// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Linq;
using ILLink.Shared.DataFlow;
using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>;

// This is needed due to NativeAOT which doesn't enable nullable globally yet
#nullable enable

namespace ILLink.Shared.TrimAnalysis
{
    internal sealed partial record ArrayValue : SingleValue
    {
        private static ValueSetLattice<SingleValue> MultiValueLattice => default;

        public readonly SingleValue Size;

        public partial bool TryGetValueByIndex(int index, out MultiValue value);

        public static MultiValue SanitizeArrayElementValue(MultiValue input)
        {
            // We need to be careful about self-referencing arrays. It's easy to have an array which has one of the elements as itself:
            // var arr = new object[1];
            // arr[0] = arr;
            //
            // We can't deep copy this, as it's an infinite recursion. And we can't easily guard against it with checks to self-referencing
            // arrays - as this could be two or more levels deep.
            //
            // We need to deep copy arrays because we don't have a good way to track references (we treat everything as a value type)
            // and thus we could get bad results if the array is involved in multiple branches for example.
            // That said, it only matters for us to track arrays to be able to get integers or Type values (and we really only need relatively simple cases)
            //
            // So we will simply treat array value as an element value as "too complex to analyze" and give up by storing Unknown instead

            bool needSanitization = false;
            foreach (var v in input.AsEnumerable())
            {
                if (v is ArrayValue)
                    needSanitization = true;
            }

            if (!needSanitization)
                return input;

            return new(input.AsEnumerable().Select(v => v is ArrayValue ? UnknownValue.Instance : v));
        }
    }
}
